package com.booter.ws.handler;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author lcw
 * @date 2022/6/16 13:54
 */
@Component
@Slf4j
public class WebSocketChannelInterceptor implements ChannelInterceptor {

    /**
     * spring boot 2.5 正常引入 ，从2.6开始spring boot 禁用了循环依赖，可以手工打开 spring.main.allow-circular-references=true
     */
    @Resource
    @Lazy
    private SimpMessagingTemplate messageTemplate;

    //用于标记消息
    public static String WS_HEADER_MESSAGE_ID = "MESSAGE_ID";
    public static String WS_HEADER_MESSAGE_TYPE = "MESSAGE_TYPE";
    public static String WS_HEADER_MESSAGE_TRACE_ID = "MESSAGE_TRACE_ID";

    public static String WS_HEADER_HTTP_SESSION_ID = "HTTP.SESSION.ID";
    public static String WS_HEADER_AUTHORIZATION = "Authorization";

    /***
     * redis 队列
     */
    public static String REDIS_MESSAGE_TOPIC = "ROUTE_MESSAGE";

    /***
     *  后端广播消息通道
     */
    public static String WEB_SOCKET_BROADCAST = "/topic/ws/broadcast";


    /***
     *  后端向个人推送消息的通道 template 自己拼接成 /user/{0}/reply
     */
    public static String WEB_SOCKET_PERSONAL_BROADCAST = "/reply";

    /**
     * @param message
     * @param messageChannel
     * @return
     */
    @Override
    public Message<?> preSend(Message<?> message, MessageChannel messageChannel) {
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        String user = "";
        Object o = accessor.getSessionAttributes().get(WS_HEADER_HTTP_SESSION_ID);
        String sessionId = o != null ? o.toString() : accessor.getSessionId();
        //心跳消息不打印
        if (accessor.getCommand() != null) {
            log.info("Request session:{},command:{}", sessionId, accessor.getCommand());
        }
        if (StompCommand.CONNECT.equals(accessor.getCommand())) {
            List<String> tokens = accessor.getNativeHeader(WS_HEADER_AUTHORIZATION);
            //user = CollectionUtils.isEmpty(tokens) ? "" : JwtUtil.getClaim(tokens.get(0), Constants.ACCOUNT);
            log.info("用户:{},sessionId:{}", user, sessionId);
            //transferService.register(user, sessionId);
            log.info("用户[{}]上线", user);
        } else if (StompCommand.DISCONNECT.equals(accessor.getCommand())) {
            log.info("用户已经下线");
//            log.info("用户[{}]已经下线", transferService.getUser(sessionId));
            //transferService.offLine(sessionId);
        } else if (StompCommand.ACK.equals(accessor.getCommand())) {
            //一 订阅回执 客户端ACK表示已经收到消息,无需再单独提交请求
            // user = transferService.getUser(sessionId);
            log.info("ack");
            List<String> messageIds = accessor.getNativeHeader(WS_HEADER_MESSAGE_ID);
            List<String> traceIds = accessor.getNativeHeader(WS_HEADER_MESSAGE_TRACE_ID);
            List<String> messageTypes = accessor.getNativeHeader(WS_HEADER_MESSAGE_TYPE);
//            if (!CollectionUtils.isEmpty(messageIds) && !CollectionUtils.isEmpty(traceIds) && !CollectionUtils.isEmpty(messageTypes)) {
//                transferService.ack(user, messageIds.get(0), Long.valueOf(traceIds.get(0)), messageTypes.get(0));
//            }
        }
        return message;
    }


    /*
     * 1. 在消息发送完成后调用，而不管消息发送是否产生异常，在次方法中，我们可以做一些资源释放清理的工作
     * 2. 此方法的触发必须是preSend方法执行成功，且返回值不为null,发生了实际的消息推送，才会触发
     */
    @Override
    public void afterSendCompletion(Message<?> message, MessageChannel messageChannel, boolean b, Exception e) {

    }

    /* 1. 在消息被实际检索之前调用，如果返回false,则不会对检索任何消息，只适用于(PollableChannels)，
     * 2. 在webSocket的场景中用不到
     */
    @Override
    public boolean preReceive(MessageChannel messageChannel) {
        return true;
    }

    /*
     * 1. 在检索到消息之后，返回调用方之前调用，可以进行信息修改，如果返回null,就不会进行下一步操作
     * 2. 适用于PollableChannels，轮询场景
     */
    @Override
    public Message<?> postReceive(Message<?> message, MessageChannel messageChannel) {
        return message;
    }

    /*
     * 1. 在消息接收完成之后调用，不管发生什么异常，可以用于消息发送后的资源清理
     * 2. 只有当preReceive 执行成功，并返回true才会调用此方法
     * 2. 适用于PollableChannels，轮询场景
     */
    @Override
    public void afterReceiveCompletion(Message<?> message, MessageChannel messageChannel, Exception e) {

    }
}
